#include "kernel.h"
#include "config.h"
#include "globals.h"
#include "errno.h"

#include "util/debug.h"
#include "util/list.h"
#include "util/string.h"
#include "util/printf.h"

#include "proc/kthread.h"
#include "proc/proc.h"
#include "proc/sched.h"
#include "proc/proc.h"

#include "mm/slab.h"
#include "mm/page.h"
#include "mm/mmobj.h"
#include "mm/mm.h"
#include "mm/mman.h"


#include "vm/vmmap.h"

#include "fs/vfs.h"
#include "fs/vfs_syscall.h"
#include "fs/vnode.h"
#include "fs/file.h"

proc_t *curproc = NULL; /* global */
static slab_allocator_t *proc_allocator = NULL;

static list_t _proc_list;
static proc_t *proc_initproc = NULL; /* Pointer to the init process (PID 1) */

#define bufSize  1000
#define uninitialed -1000
#define initialed 1000
static char printBuf[bufSize];

void
proc_init()
{
        list_init(&_proc_list);
        proc_allocator = slab_allocator_create("proc", sizeof(proc_t));
        KASSERT(proc_allocator != NULL);
}

static pid_t next_pid = 0;

/**
 * Returns the next available PID.
 *
 * Note: Where n is the number of running processes, this algorithm is
 * worst case O(n^2). As long as PIDs never wrap around it is O(n).
 *
 * @return the next available PID
 */
static int
_proc_getid()
{
        proc_t *p;
        pid_t pid = next_pid;
        while (1) {
failed:
                list_iterate_begin(&_proc_list, p, proc_t, p_list_link) {
                        if (p->p_pid == pid) {
                                if ((pid = (pid + 1) % PROC_MAX_COUNT) == next_pid) {
                                        return -1;
                                } else {
                                        goto failed;
                                }
                        }
                } list_iterate_end();
                next_pid = (pid + 1) % PROC_MAX_COUNT;
                return pid;
        }
}


void freeStorageOfProcess(proc_t* deadProc)
{
	KASSERT(NULL != deadProc->p_pagedir);
	int pid = deadProc->p_pid;
	dbg(DBG_PROC,"(GRADING 2.c)proc %s(pid:%d)has pagedir\n",deadProc->p_comm,deadProc->p_pid);
	pt_destroy_pagedir(deadProc->p_pagedir);
	slab_obj_free(proc_allocator, deadProc);
	dbg(DBG_MM,"proc (PID: %d) destroyed cleanly\n",pid);
}
/*return the status*/
void processDeadChildProcess(proc_t* childProc,int* status)
{
    kthread_t *myThread;

	if(childProc->p_state==PROC_DEAD)
	{
		*status=childProc->p_status;
		list_iterate_begin(&(childProc->p_threads),myThread,kthread_t,kt_plink)
        	{
			if(myThread->kt_state==KT_EXITED)
			{
				KASSERT(KT_EXITED == myThread->kt_state);
				dbg(DBG_PROC,"(GRADING 2.c)thread of proc %s(pid:%d)is to be destroyed\n",myThread->kt_proc->p_comm,myThread->kt_proc->p_pid);
				kthread_destroy(myThread);
        		}
      		}
		list_iterate_end();
         	list_remove(&(childProc->p_child_link));
         	freeStorageOfProcess(childProc);
	 }
}

void appendChildToIniProc(proc_t* myProc)
{
	proc_t* procPonter;
	/*give the children  to idle process*/
	if(!list_empty(&(myProc->p_children)))
	{
        list_iterate_begin(&(myProc->p_children), procPonter, proc_t, p_child_link)
        {
            list_insert_tail(&(proc_initproc->p_children),&(procPonter->p_child_link));
            procPonter->p_pproc=proc_initproc;
        }
        list_iterate_end();
	}
}
/*
 * The new process, although it isn't really running since it has no
 * threads, should be in the PROC_RUNNING state.
 *
 * Don't forget to set proc_initproc when you create the init
 * process. You will need to be able to reference the init process
 * when reparenting processes to the init process.
 */
proc_t *
proc_create(char *name)
{
	/*6.21：p_wait不知道是做什么的*/
	proc_t *newprocess;
	newprocess = (proc_t *)slab_obj_alloc(proc_allocator);
	memset(newprocess, 0, sizeof(proc_t));

	int pid=_proc_getid();
    KASSERT(PID_IDLE != pid || list_empty(&_proc_list));
    KASSERT(PID_INIT != pid || PID_IDLE == curproc->p_pid);
    dbg(DBG_PROC,"(GRADING 2.a)newly created proc with pid:%d",pid);
	newprocess->p_pid = pid;


	strcpy(newprocess->p_comm, name);
	list_init(&(newprocess->p_threads));
	list_init(&(newprocess->p_children));
	newprocess->p_pagedir = pt_create_pagedir();
	list_insert_tail(&_proc_list, &newprocess->p_list_link);
	/*什么的queue for wait?*/
	sched_queue_init(&(newprocess->p_wait));
	if(newprocess->p_pid == PID_IDLE){
		newprocess->p_pproc = NULL;
	}
	else{
		if(newprocess->p_pid == PID_INIT){
			proc_initproc = newprocess;
		}
		newprocess->p_pproc = curproc;
		list_insert_tail(&curproc->p_children, &newprocess->p_child_link);
	}

	newprocess->p_state = PROC_RUNNING;
	/*退出状态，0？*/
	newprocess->p_status = 0;
	newprocess->p_cwd = vfs_root_vn;
        if (newprocess->p_cwd!=NULL)
        {
            vref(curproc->p_cwd);
        }
        /*int fd=0;
        while(!(fd<NFILES))
        {
            process->p_files[fd] = NULL;
	    fd++;
	}*/
	newprocess->p_vmmap=vmmap_create();
	newprocess->p_vmmap->vmm_proc=newprocess;
	return newprocess;

        /*NOT_YET_IMPLEMENTED("PROCS: proc_create");*/

}

/**
 * Cleans up as much as the process as can be done from within the
 * process. This involves:
 *    - Closing all open files (VFS)
 *    - Cleaning up VM mappings (VM)
 *    - Waking up its parent if it is waiting
 *    - Reparenting any children to the init process
 *    - Setting its status and state appropriately
 *
 * The parent will finish destroying the process within do_waitpid (make
 * sure you understand why it cannot be done here). Until the parent
 * finishes destroying it, the process is informally called a 'zombie'
 * process.
 *
 * This is also where any children of the current process should be
 * reparented to the init process (unless, of course, the current
 * process is the init process. However, the init process should not
 * have any children at the time it exits).
 *
 * Note: You do _NOT_ have to special case the idle process. It should
 * never exit this way.
 *
 * @param status the status to exit the process with
 */
void
proc_cleanup(int status)
{
	KASSERT(NULL != proc_initproc);
	dbg(DBG_PROC,"(GRADING 2.b)have a initproc %s(pid:%d)\n",proc_initproc->p_comm,proc_initproc->p_pid);
	KASSERT(NULL != curproc->p_pproc);
	dbg(DBG_PROC,"(GRADING 2.b)parent of curproc is %s(pid:%d)\n",curproc->p_pproc->p_comm,curproc->p_pproc->p_pid);
	KASSERT(1 <= curproc->p_pid);
	dbg(DBG_PROC,"(GRADING 2.b)curproc %s(pid:%d)is not idle_proc\n",curproc->p_comm,curproc->p_pid);
	KASSERT(curproc!=NULL);
	KASSERT(1 <= curproc->p_pid);
	proc_t *myProc;

    curproc->p_state=PROC_DEAD;
    curproc->p_status=status;
	/*waking up parents*/
	if((curproc->p_pproc!=NULL)&&curproc->p_pproc->p_wait.tq_size!=0)
	{
		sched_wakeup_on( &(curproc->p_pproc->p_wait));
	}

	appendChildToIniProc(curproc);
    list_remove(&(curproc->p_list_link));
    KASSERT(NULL != curproc->p_pproc);
    dbg(DBG_PROC,"(GRADING 2.b)parent of curproc is %s(pid:%d)\n",curproc->p_pproc->p_comm,curproc->p_pproc->p_pid);
    int fd;
    for(fd=0;fd<NFILES;fd++)
    {
	 if(curproc->p_files[fd]!=NULL)
	{    dbg(DBG_TEST,"name %s,fd %d,ref %d\n",curproc->p_comm,fd,curproc->p_files[fd]->f_refcount);
	    if(curproc->p_files[fd]->f_refcount>0)
	    { 
		do_close(fd);    
	    }    
	}    
	
    }
    if(curproc->p_cwd!=NULL)
    	vput(curproc->p_cwd);
    sched_switch();



}

/*
 * This has nothing to do with signals and kill(1).
 *
 * Calling this on the current process is equivalent to calling
 * do_exit().
 *
 * In Weenix, this is only called from proc_kill_all.
 */
void
proc_kill(proc_t *p, int status)
{
	kthread_t *thr;
	if(p==curproc)
		do_exit(status);
	else
	{
		list_iterate_begin(&p->p_threads,thr,kthread_t,kt_plink)
		{
			if(thr->kt_state!=KT_EXITED)
			kthread_cancel(thr,&status);
		}list_iterate_end();
		
	}
	p->p_status=status;
}

/*
 * Remember, proc_kill on the current process will _NOT_ return.
 * Don't kill direct children of the idle process.
 *
 * In Weenix, this is only called by sys_halt.
 */
void
proc_kill_all()
{
	proc_t *myProc;
    dbg_print("Enter proc_kill_all, proc_list:\n %s", printBuf);
    proc_list_info(NULL, printBuf, bufSize);

	 list_iterate_begin(&_proc_list, myProc, proc_t, p_list_link)
	{
		 if(myProc->p_pproc!=NULL
			&&myProc->p_pid!=PID_IDLE
			&&myProc->p_pproc->p_pid!=PID_IDLE)
		 {
			 if(myProc != curproc){
				 proc_kill(myProc,0);
			 }
		 }
	}
	 list_iterate_end();
	 proc_kill(curproc,0);
	   proc_list_info(NULL, printBuf, bufSize);

	    dbg_print("Left proc_kill_all, proc_list:\n %s", printBuf);
	    proc_list_info(NULL, printBuf, bufSize);
}

proc_t *
proc_lookup(int pid)
{
        proc_t *p;
        list_iterate_begin(&_proc_list, p, proc_t, p_list_link) {
                if (p->p_pid == pid) {
                        return p;
                }
        } list_iterate_end();
        return NULL;
}

list_t *
proc_list()
{
        return &_proc_list;
}

/*
 * This function is only called from kthread_exit.
 *
 * Unless you are implementing MTP, this just means that the process
 * needs to be cleaned up and a new thread needs to be scheduled to
 * run. If you are implementing MTP, a single thread exiting does not
 * necessarily mean that the process should be exited.
 */
void
proc_thread_exited(void *retval)
{
	 proc_cleanup(0);
}

/* If pid is -1 dispose of one of the exited children of the current
 * process and return its exit status in the status argument, or if
 * all children of this process are still running, then this function
 * blocks on its own p_wait queue until one exits.
 *
 * If pid is greater than 0 and the given pid is a child of the
 * current process then wait for the given pid to exit and dispose
 * of it.
 *
 * If the current process has no children, or the given pid is not
 * a child of the current process return -ECHILD.
 *
 * Pids other than -1 and positive numbers are not supported.
 * Options other than 0 are not supported.
 */
pid_t
do_waitpid(pid_t pid, int options, int *status)
{
	proc_t *myProc;
	KASSERT(pid==-1||pid>0);
	int tempStatus=uninitialed;
	list_link_t *__link;                                    
        list_link_t *__next;
	/*status=(int *)kmalloc(sizeof(int));*/
	while(1)
	{
		int myPid=uninitialed;
		if(!(list_empty(&(curproc->p_children))))
		{
			if(pid==-1)
			{                              
                                
                                for(__link = (&(curproc->p_children))->l_next;__link != (&(curproc->p_children));__link=__next)
				/*list_iterate_begin(&curproc->p_children,myProc,proc_t,p_child_link)*/
				{
					
					myProc=list_item(__link,proc_t,p_child_link);
					KASSERT(NULL != myProc);
					dbg(DBG_PROC,"(GRADING 2.c)waiting proc %s(pid:%d)is not NULL\n",myProc->p_comm,myProc->p_pid);
					KASSERT(-1 == pid || myProc->p_pid == pid);
					dbg(DBG_PROC,"(GRADING 2.c)pid:%d,find proc %s(pid:%d)\n",pid,myProc->p_comm,myProc->p_pid);
					
						myPid=myProc->p_pid;
					/*KASSERT(pid == myProc->p_pid);*/
					
					
						if(myProc->p_state==(int)PROC_DEAD)
						{
							processDeadChildProcess(myProc,&tempStatus);
							break;
						}
					
				__next = __link->l_next;  
				}
				/*list_iterate_end();*/
			}
			else
			{
				for(__link = (&(curproc->p_children))->l_next;__link != (&(curproc->p_children));__link=__next)
				{
					myProc=list_item(__link,proc_t,p_child_link);
					if(myProc->p_pid==pid)
					{
						KASSERT(-1 == pid || myProc->p_pid == pid);
						dbg(DBG_PROC,"(GRADING 2.c)pid:%d,find proc %s(pid:%d)\n",pid,myProc->p_comm,myProc->p_pid);
						myPid=myProc->p_pid;
						if(myProc->p_state==PROC_DEAD)
						{
							processDeadChildProcess(myProc,&tempStatus);
							break;
						}
					}
				__next = __link->l_next; 
				}
				

				if(myPid==uninitialed)
				{
					return -ECHILD;
				}
			}
		}
		else
		{
			return -ECHILD;
		}

		if(tempStatus!=uninitialed)
		{
			if(status!=NULL)
				*status=tempStatus;
			return myPid;
		}
		else
		{
			sched_sleep_on(&curproc->p_wait);
		}
	}
	return 0;
}


/*
 * Cancel all threads, join with them, and exit from the current
 * thread.
 *
 * @param status the exit status of the process
 */
void
do_exit(int status)
{
	/*6.27*/
		kthread_t *thr=NULL;
		kthread_exit(0);
		curproc->p_status=status;
	        /*NOT_YET_IMPLEMENTED("PROCS: do_exit");*/
}

size_t
proc_info(const void *arg, char *buf, size_t osize)
{
        const proc_t *p = (proc_t *) arg;
        size_t size = osize;
        proc_t *child;

        KASSERT(NULL != p);
        KASSERT(NULL != buf);

        iprintf(&buf, &size, "pid:          %i\n", p->p_pid);
        iprintf(&buf, &size, "name:         %s\n", p->p_comm);
        if (NULL != p->p_pproc) {
                iprintf(&buf, &size, "parent:       %i (%s)\n",
                        p->p_pproc->p_pid, p->p_pproc->p_comm);
        } else {
                iprintf(&buf, &size, "parent:       -\n");
        }

#ifdef __MTP__
        int count = 0;
        kthread_t *kthr;
        list_iterate_begin(&p->p_threads, kthr, kthread_t, kt_plink) {
                ++count;
        } list_iterate_end();
        iprintf(&buf, &size, "thread count: %i\n", count);
#endif

        if (list_empty(&p->p_children)) {
                iprintf(&buf, &size, "children:     -\n");
        } else {
                iprintf(&buf, &size, "children:\n");
        }
        list_iterate_begin(&p->p_children, child, proc_t, p_child_link) {
                iprintf(&buf, &size, "     %i (%s)\n", child->p_pid, child->p_comm);
        } list_iterate_end();

        iprintf(&buf, &size, "status:       %i\n", p->p_status);
        iprintf(&buf, &size, "state:        %i\n", p->p_state);

#ifdef __VFS__
#ifdef __GETCWD__
        if (NULL != p->p_cwd) {
                char cwd[256];
                lookup_dirpath(p->p_cwd, cwd, sizeof(cwd));
                iprintf(&buf, &size, "cwd:          %-s\n", cwd);
        } else {
                iprintf(&buf, &size, "cwd:          -\n");
        }
#endif /* __GETCWD__ */
#endif

#ifdef __VM__
        iprintf(&buf, &size, "start brk:    0x%p\n", p->p_start_brk);
        iprintf(&buf, &size, "brk:          0x%p\n", p->p_brk);
#endif

        return size;
}

size_t
proc_list_info(const void *arg, char *buf, size_t osize)
{
        size_t size = osize;
        proc_t *p;

        KASSERT(NULL == arg);
        KASSERT(NULL != buf);

#if defined(__VFS__) && defined(__GETCWD__)
        iprintf(&buf, &size, "%5s %-13s %-18s %-s\n", "PID", "NAME", "PARENT", "CWD");
#else
        iprintf(&buf, &size, "%5s %-13s %-s\n", "PID", "NAME", "PARENT");
#endif

        list_iterate_begin(&_proc_list, p, proc_t, p_list_link) {
                char parent[64];
                if (NULL != p->p_pproc) {
                        snprintf(parent, sizeof(parent),
                                 "%3i (%s)", p->p_pproc->p_pid, p->p_pproc->p_comm);
                } else {
                        snprintf(parent, sizeof(parent), "  -");
                }

#if defined(__VFS__) && defined(__GETCWD__)
                if (NULL != p->p_cwd) {
                        char cwd[256];
                        lookup_dirpath(p->p_cwd, cwd, sizeof(cwd));
                        iprintf(&buf, &size, " %3i  %-13s %-18s %-s\n",
                                p->p_pid, p->p_comm, parent, cwd);
                } else {
                        iprintf(&buf, &size, " %3i  %-13s %-18s -\n",
                                p->p_pid, p->p_comm, parent);
                }
#else
                iprintf(&buf, &size, " %3i  %-13s %-s\n",
                        p->p_pid, p->p_comm, parent);
#endif
        } list_iterate_end();
        return size;
}
